import asyncio
import socketserver
from dataclasses import dataclass
from time import time


@dataclass
class PlayerInfo:
    room = None
    recv: str = ""
    send: str = ""
    ready = False
    roommate = None
    character = None


class RoomInfo:
    def __init__(self):
        self.player_info: list[PlayerInfo] = []
        self.seed = str(int(time()))

    def fresh(self):
        self.seed = str(int(time()))


rooms = [RoomInfo() for i in range(5)]


class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
    async def async_recv(self):
        while True:
            data = self.rfile.readline().decode().strip()

            if not data:
                await asyncio.sleep(0.1)
            else:
                while self.player_info.recv:
                    pass
                self.player_info.recv = data

    async def async_send(self):
        while True:
            if not self.player_info.send:
                await asyncio.sleep(0.1)
            else:
                msg = self.player_info.send
                if not isinstance(msg, str):
                    msg = str(msg)
                self.wfile.write(msg.encode())
                self.player_info.send = ""

    async def async_process(self):
        def handle_recv():
            if (self.player_info.recv) == "get_room":
                room_player_nums = [len(room.player_info) for room in rooms]
                self.player_info.send = str(room_player_nums)
                print("get_room")
            elif self.player_info.recv.startswith("get_in_room"):
                if self.player_info.room is not None:
                    rooms[self.player_info.room].player_info.remove(self.player_info)
                self.player_info.room = int(str(self.player_info.recv[-1]))
                rooms[self.player_info.room].player_info.append(self.player_info)
                print("get_in_room")
            elif (self.player_info.recv) == "room_ready":
                self.player_info.ready = True
                if len(rooms[self.player_info.room].player_info) == 2 and rooms[self.player_info.room].player_info[0].ready \
                        and rooms[self.player_info.room].player_info[1].ready:
                    rooms[self.player_info.room].player_info[0].roommate = rooms[self.player_info.room].player_info[1]
                    rooms[self.player_info.room].player_info[1].roommate = rooms[self.player_info.room].player_info[0]
                    rooms[self.player_info.room].player_info[0].send = "game_begin"
                    rooms[self.player_info.room].player_info[1].send = "game_begin"
                    rooms[self.player_info.room].fresh()
                print("game_begin")
            elif self.player_info.recv.startswith("choose_character"):
                self.player_info.character = self.player_info.recv.split(":")[1]
                if self.player_info.roommate.character:
                    rooms[self.player_info.room].player_info[0].send = [rooms[self.player_info.room].player_info[0].character,
                                                                        rooms[self.player_info.room].player_info[1].character, 0]
                    rooms[self.player_info.room].player_info[1].send = [rooms[self.player_info.room].player_info[0].character,
                                                                        rooms[self.player_info.room].player_info[1].character, 1]
                print(f"{self.player_info.character}")
            elif self.player_info.recv.startswith("get_seed"):
                self.player_info.send = rooms[self.player_info.room].seed
            else:
                try:
                    self.player_info.roommate.send = self.player_info.recv
                except Exception as e:
                    print(e)

            self.player_info.recv = None

        while True:
            if self.player_info.recv:
                handle_recv()
            await asyncio.sleep(0.1)

    async def async_handle(self):
        task_recv = asyncio.create_task(self.async_recv())
        task_send = asyncio.create_task(self.async_send())
        task_process = asyncio.create_task(self.async_process())

        done, pending = await asyncio.wait((task_recv, task_send, task_process), return_when=asyncio.FIRST_EXCEPTION)
        for task in pending:
            task.cancel()

    def handle(self):
        self.player_info = PlayerInfo()
        asyncio.run(self.async_handle())

    def finish(self) -> None:
        print(f"角色已退出{self.player_info.character}")
        if self.player_info.room:
            for player in rooms[self.player_info.room].player_info:
                player.roommate = None
        if self.player_info.room is not None:
            rooms[self.player_info.room].player_info.remove(self.player_info)
        super().finish()


def run_server(ip, port):
    with socketserver.ThreadingTCPServer((ip, port), ThreadedTCPRequestHandler) as server:
        server.daemon_threads = True
        server.socket.setblocking(False)
        print("Server begin")
        server.serve_forever()
        print("Server end")


# run_server("localhost", 9998)
run_server("10.0.20.4", 9998)
